#include "web_client.hpp"

#include <QDebug>
#include <QStringList>
#include <QBuffer>
#include <QIODevice>
#include <QEventLoop>
#include <QUrl>
#include <QMap>
#include <QList>
#include <QNetworkReply>
#include <QNetworkRequest>
#include <QNetworkAccessManager>

WebClient::WebClient(SynchronizationMode mode, QObject* parent) : QNetworkAccessManager(parent)
{
    _init(mode);
}

WebClient::~WebClient()
{
    delete _eventLoop;
    delete _connections;
}

void WebClient::_init(SynchronizationMode mode)
{

    _eventLoop = new QEventLoop();
    _connections = new QMap<QString, Request>();
    synchronizationMode = mode;
    connect(this, SIGNAL(finished(QNetworkReply*)), SLOT(_replyFinished(QNetworkReply*)));
}

void WebClient::_replyFinished(QNetworkReply* reply)
{
    QString url = reply->url().toString();
    Request request = _connections->value(url);
    if (reply->error() == QNetworkReply::NoError)
        request.status = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
    else
        request.status = reply->error();
    request.buffer->open(QBuffer::ReadWrite);
    request.buffer->write(reply->read(reply->size()));
    if (synchronizationMode == Synchronous){
        _eventLoop->exit();
        // We update the request associated to the URL
        _connections->insert(url, request);
    }
    else
        _connections->remove(url);
    emit httpRequestFinished(url, request.status, request.buffer);
}

/*!
    * \brief Store requests to handle them when finished
    *
    * \param url URL of the associated request.
    * \param buffer Buffer where data will be stored.
*/
void WebClient::_registerRequest(QString url, QIODevice *buffer)
{
    Request req;
    req.buffer = buffer;
    req.status = QNetworkReply::NoError;
    req.url = url;
    _connections->insert(url, req);
}

/*!
    * \brief Create request
    *
    * \param operation Get/Post method
    * \param _url URL to fetch
    * \param args Args associated to the POST method
    * \param to IODevice where to store data.
*/
int WebClient::_makeRequest(Operation operation, QString _url, QString args, QIODevice* to)
{
    QUrl url = QUrl(_url);
    QNetworkRequest request = QNetworkRequest(url);
    QByteArray data = QByteArray(args.toUtf8());
    QNetworkReply *reply;
    int status = QNetworkReply::NoError;

    switch (operation){
        case GetOperation:
            reply = QNetworkAccessManager::get(request);
            break;
        case PostOperation:
            reply = QNetworkAccessManager::post(request, data);
            break;
        default:
            return -1;
            break;
    }

    _registerRequest(_url, to);

    if (synchronizationMode == Synchronous){
        // We wait that request is finished to continue.
        _eventLoop->exec();
        status = _connections->value(_url).status;
        _connections->remove(_url);
    }
    return status;
}

int WebClient::get(QString url, QIODevice* to)
{
    return _makeRequest(GetOperation, url, QString(), to);
}

int WebClient::post(QString url, QMap<QString, QString> args, QIODevice* to)
{
    QStringList argList = QStringList();
    QMap<QString, QString>::iterator i;

    for (i = args.begin(); i != args.end(); i++)
        argList.append(i.key() + "=" + i.value());
    return post(url, argList, to);
}

int WebClient::post(QString url, QStringList args, QIODevice* to)
{
    return _makeRequest(PostOperation, url, args.join("&"), to);
}
